﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Security.Principal;
using System.Web.Mvc;
using Universum.Model.Domain.Classes;
using Universum.Model.Services.Classes;
using Universum.Views.Classes;
using Universum.Infrastructure.Utils;
using Universum.Model.Services;
using Universum.Infrastructure.Configuration;
using Universum.Model.Services.MyClasses;

namespace Universum.Controllers
{
	[Authorize(Roles=ConstantIdentifiers.ROLE_OF_TEACHER)]
	public class ClassesController : UniversumController
	{
		IPageGroupService _PageGroupService;
		IPageArticleService _PageArticleService;
		IClassesService _ClassesService;
		ILabWorkArticleService _LabWorkArticleService;
		IFriendlyUrlService _SlugService;
		ISubmissionService _SubmissionService;
		public ClassesController(
			IPrincipal principal,
			IFriendlyUrlService slugService,
			IPageArticleService pageArticleService,
			IPageGroupService pageGroupService,
			IClassesService classesService,
			ILabWorkArticleService labworkArticleService,
			ISubmissionService submissionService
			)
			: base(principal)
		{
			_PageGroupService = pageGroupService;
			_PageArticleService = pageArticleService;
			_ClassesService = classesService;
			_SlugService = slugService;
			_LabWorkArticleService = labworkArticleService;
			_SubmissionService = submissionService;
		}


		public ActionResult Index()
		{
			ClassesIndexViewModel model = new ClassesIndexViewModel();
			model.Classes = _ClassesService.GetClassesForUser(User.Identity.Name);
		

			ViewData.Model = model;
			return View();
		}

		#region Class
		public ActionResult CreateClass()
		{
			return View();
		}
		[ValidateInput(false)]
		[AcceptVerbs(HttpVerbs.Post)]
		public ActionResult CreateClass(
		[Bind(Include = "Name,Description,IsActive")]	
			Class @class)
		{
			if (this.ModelState.IsValid.IsTrue())
			{
				Class save = _ClassesService.CreateNewClass();
				save.Name = @class.Name;
				save.Description = @class.Description;
				save.IsActive = @class.IsActive;
				_ClassesService.SaveOrUpdateClass(save);
				return RedirectToAction<ClassesController>(c => c.Index());
			}
			return View();
		}

		public ActionResult EditClass(Guid id)
		{
			var model = _ClassesService.GetClassById(id);
			if (model == null) return RedirectToAction<ErrorController>(it => it.FileNotFound());
			ViewData.Model = model;
			return View();
		}
		[AcceptVerbs(HttpVerbs.Post)]
		[ValidateInput(false)]
		public ActionResult EditClass(Class @class)
		{
			if (ModelState.IsValid)
			{
				var update = _ClassesService.GetClassById(@class.ID);
				update.Name = @class.Name;
				update.Description = @class.Description;
				update.IsActive = @class.IsActive;
				_ClassesService.SaveOrUpdateClass(update);

				return RedirectToAction<ClassesController>(c => c.Show(@class.ID));
			}
			return View();
		}

		public ActionResult Show(Guid id)
		{
			ViewData.Model = _ClassesService.GetClassById(id);
			return View();
		}
		[AcceptVerbs(HttpVerbs.Post)]
		public ActionResult RemoveClass(Guid id)
		{
			var model = _ClassesService.GetClassById(id);
			if (model != null && User.Identity.Name == model.Owner)
			{
				_ClassesService.Remove(model);
			}
			return RedirectToAction<ClassesController>(c => c.Index());
		}
		#endregion

		#region Edit Or Create Content
		public ActionResult EditOrCreateContent(Guid id)
		{
			PageGroup uncategorizedGroup = _PageGroupService.GetUncategorizedPageGroupByClassId(id);
			


			ClassesEditOrCreateContentViewModel model = new ClassesEditOrCreateContentViewModel();
			//model.ClassId = id;
			model.Class = _ClassesService.GetClassById(id);
			model.Root = new PageGroupNode();

			var categories = _PageGroupService.GetPageGroupsByClassId(id);

			var rootNodes = categories.Where(page_group => page_group.ParentGroupId == Guid.Empty).ToList();

			model.Root.Nodes = (from node in categories
								where node.ParentGroupId == Guid.Empty
								orderby node.Order
								select new PageGroupNode
								{
									PageGroup = node,
									ArticleList = _PageArticleService.GetArticlesByPageGroupId(node.ID)

								}).ToList();

			FillRecursive(model.Root.Nodes.ToList(), categories);

			ViewData.Model = model;
			return View();
		}

		private void FillRecursive(List<PageGroupNode> list, IList<PageGroup> categories)
		{
			list.ForEach(
				item =>
				{
					item.Nodes = (from node in categories
								  where node.ParentGroupId == item.PageGroup.ID
								  orderby node.Order
								  select new
								  PageGroupNode
								  {
									  PageGroup = node,
									  ArticleList = _PageArticleService.GetArticlesByPageGroupId(node.ID)

								  }
								  ).ToList();
					FillRecursive(item.Nodes.ToList(), categories);
				});


		}
		#endregion

		#region PageArticle
		public ActionResult CreatePageArticle(Guid id/*class id*/)
		{
			create_page_group_elements(id, null);


			return View();
		}

		[AcceptVerbs(HttpVerbs.Post)]
		[ValidateInput(false)]
		public ActionResult CreatePageArticle(

			FormCollection
			values)
		{
			var newArticle = _PageArticleService
				.CreateNew();

			TryUpdateModel(newArticle, new string[]{
				MemberUtil<PageArticle>.MemberName(m=>m.PageGroupId),
				MemberUtil<PageArticle>.MemberName(m=>m.Text),
				MemberUtil<PageArticle>.MemberName(m=>m.Title),
				MemberUtil<PageArticle>.MemberName(m=>m.Publish)
			}, values.ToValueProvider());

			if (ModelState.IsValid)
			{
				_PageArticleService.SaveOrUpdate(newArticle);
				var pageGroup = _PageGroupService.GetPageGroupById(newArticle.PageGroupId);
				Guid id = pageGroup.ClassId;
				return RedirectToAction<ClassesController>(
					c => c.EditOrCreateContent(id));
			}
			return View();
		}
		public ActionResult Article(string id)
		{
			var model = _PageArticleService.GetArticleBySlug(id);
			ViewData.Model = model;
			return View();
		}
		public ActionResult EditArticle(string id)
		{
			var model = _PageArticleService.GetArticleBySlug(id);
			if (model == null) RedirectToAction<ClassesController>(
				 c => c.Index());
			var pageClassId = _PageGroupService.GetPageGroupById(model.PageGroupId);

			create_page_group_elements(pageClassId.ClassId, pageClassId.Name);

			ViewData.Model = model;
			return View();
		}


		[AcceptVerbs(HttpVerbs.Post)]
		[ValidateInput(false)]
		public ActionResult UpdateArticle(FormCollection values)
		{
			var update = _PageArticleService.GetArticleById(
			new Guid(
			values[MemberUtil<PageArticle>.MemberName(m => m.ID)]));
			if (update == null) return RedirectToAction<ClassesController>(c => c.Index());
			TryUpdateModel(update, new string[]{
				MemberUtil<PageArticle>.MemberName(m=>m.ID),
				MemberUtil<PageArticle>.MemberName(m=>m.PageGroupId),
				MemberUtil<PageArticle>.MemberName(m=>m.Text),
				MemberUtil<PageArticle>.MemberName(m=>m.Title),
				MemberUtil<PageArticle>.MemberName(m=>m.Publish)
			}, values.ToValueProvider());

			if (ModelState.IsValid)
			{
				_PageArticleService.SaveOrUpdate(update);
				var pageGroup = _PageGroupService.GetPageGroupById(update.PageGroupId);
				return RedirectToAction<ClassesController>(
					c => c.EditOrCreateContent(pageGroup.ClassId));

			}
			var _pageGroup = _PageGroupService.GetPageGroupById(update.PageGroupId);

			create_page_group_elements(_pageGroup.ClassId, null);
			return View();
		}

		[AcceptVerbs(HttpVerbs.Post)]
		public ActionResult RemoveArticle(Guid id)
		{
			var entity = _PageArticleService.GetArticleById(id);
			
			if (entity != null && entity.Username == User.Identity.Name)
			{
				var classId = _PageGroupService.GetPageGroupById(entity.PageGroupId).ClassId;
				_PageArticleService.Remove(entity);
				return RedirectToAction<ClassesController>(c => c.EditOrCreateContent(classId));
			}
			
			return RedirectToAction<ClassesController>(c => c.Index());
		}
		#endregion

		#region Page groups
		public ActionResult CreatePageGroup(Guid id /*class id*/)
		{
			ViewData.Add("classId", id);
			var pageGroups = _PageGroupService.GetPageGroupsByClassId(id);
			const string topLevel = "- Top level -";
			pageGroups.Add(new PageGroup { ID = Guid.Empty, Name = topLevel });

			ViewData.Add("pageGroups", pageGroups.ToSelectListItems(topLevel));
			return View();
		}

		public ActionResult LevelDownPageGroup(Guid pageGroupID) {
			var pageGroup = _PageGroupService.GetPageGroupById(pageGroupID);

			
			_PageGroupService.LevelDown(pageGroup);
			return RedirectToAction<ClassesController>(
					c => c.EditOrCreateContent(pageGroup.ClassId));
		}

		public ActionResult LevelUpPageGroup(Guid pageGroupID)
		{
			var pageGroup = _PageGroupService.GetPageGroupById(pageGroupID);
			_PageGroupService.LevelUp(pageGroup);
			return RedirectToAction<ClassesController>(
					c => c.EditOrCreateContent(pageGroup.ClassId));
		}

		[ValidateInput(false)]
		[AcceptVerbs(HttpVerbs.Post)]
		public ActionResult CreatePageGroup(FormCollection values)
		{
			var newPageGroup = _PageGroupService.CreateNewPageGroup(
			new Guid(values[MemberUtil<PageGroup>.MemberName(m => m.ClassId)]));
			TryUpdateModel(newPageGroup, new string[]{
				MemberUtil<PageGroup>.MemberName(m=>m.Name),
				MemberUtil<PageGroup>.MemberName(m=>m.ParentGroupId),
				MemberUtil<PageGroup>.MemberName(m=>m.ClassId)
			}, values.ToValueProvider());
			if (ModelState.IsValid)
			{
				_PageGroupService.SaveOrUpdate(newPageGroup);
				return RedirectToAction<ClassesController>(
					c => c.EditOrCreateContent(newPageGroup.ClassId));
			}
			return View();
		}

		public ActionResult EditPageGroup(Guid id /*page group id*/)
		{	
			
			
			var model = _PageGroupService.GetPageGroupById(id);
			ViewData.Model = model;
			ViewData.Add("classId", model.ClassId);
			var pageGroups = _PageGroupService.GetPageGroupsByClassId(model.ClassId);
			pageGroups.Remove(model);
			const string topLevel = "- Top level -";
			pageGroups.Add(new PageGroup { ID = Guid.Empty, Name = topLevel });

			ViewData.Add("pageGroups", pageGroups.ToSelectListItems(topLevel));

			return View();
		}

		[ValidateInput(false)]
		[AcceptVerbs(HttpVerbs.Post)]
		public ActionResult UpdatePageGroup(FormCollection values)
		{
			var newPageGroup = _PageGroupService.GetPageGroupById(
			new Guid(values[MemberUtil<PageGroup>.MemberName(m => m.ID)]));

			TryUpdateModel(newPageGroup, new string[]{
				MemberUtil<PageGroup>.MemberName(m=>m.Name),
				MemberUtil<PageGroup>.MemberName(m=>m.ParentGroupId),
				
			}, values.ToValueProvider());
			if (ModelState.IsValid)
			{
				newPageGroup.Order = 0;
				_PageGroupService.SaveOrUpdate(newPageGroup);
				return RedirectToAction<ClassesController>(
					c => c.EditOrCreateContent(newPageGroup.ClassId));
			}

			ViewData.Add("classId", newPageGroup.ClassId);
			var pageGroups = _PageGroupService.GetPageGroupsByClassId(newPageGroup.ClassId);
			const string topLevel = "- Top level -";
			pageGroups.Add(new PageGroup { ID = Guid.Empty, Name = topLevel });
			ViewData.Add("pageGroups", pageGroups.ToSelectListItems(topLevel));

			return View();
		}

		[AcceptVerbs(HttpVerbs.Post)]
		public ActionResult RemovePageGroup(Guid id)
		{
			var entity = _PageGroupService.GetPageGroupById(id);

			if (entity != null && entity.Username == User.Identity.Name)
			{
				var classId = entity.ClassId;
				_PageGroupService.Remove(entity);
				return RedirectToAction<ClassesController>(c => c.EditOrCreateContent(classId));
			}
			
			return RedirectToAction<ClassesController>(c => c.Index());
		}

		#endregion

		#region LabWork
		public ActionResult CreatePageWork(Guid id/*class id*/)
		{
			create_page_group_elements(id, null);

			return View();
		}


		[AcceptVerbs(HttpVerbs.Post)]
		[ValidateInput(false)]
		public ActionResult CreatePageWork(FormCollection values)
		{
			LabWorkArticle labWork = _LabWorkArticleService.CreateNewPageWork();
			TryUpdateModel(labWork, new string[]{
				MemberUtil<LabWorkArticle>.MemberName(m=>m.PageGroupId),
				MemberUtil<LabWorkArticle>.MemberName(m=>m.Publish),
				MemberUtil<LabWorkArticle>.MemberName(m=>m.SubmissionEnd),
				MemberUtil<LabWorkArticle>.MemberName(m=>m.Text),
				MemberUtil<LabWorkArticle>.MemberName(m=>m.Title)
				
			}, values.ToValueProvider());
			var pageGroup = _PageGroupService.GetPageGroupById(labWork.PageGroupId);
			var classId = pageGroup.ClassId;
			var className = _ClassesService.GetClassById(classId).Name;
			if (ModelState.IsValid)
			{

				labWork.Slug = _SlugService.CreateSlugFrom(
					className + "_" +
					pageGroup.Name + "_" +
					labWork.Title);

				_LabWorkArticleService.SaveOrUpdate(labWork);
				return RedirectToAction<ClassesController>(
					c => c.EditOrCreateContent(classId));
			}

			create_page_group_elements(classId, null);

			return View();
		}

		public ActionResult Lab(string id)
		{
			var viewModel = new ClassesLabWorkViewModel();
			viewModel.LabWork = _LabWorkArticleService.GetLabWorkBySlug(id);
			viewModel.Submissions = _SubmissionService.GetSubmisionsByLabWorkId(viewModel.LabWork.ID);
			ViewData.Model = viewModel; 
			return View();
		}

		public ActionResult EditLabWork(string id)/* slug */
		{
			var model = _LabWorkArticleService.GetLabWorkBySlug(id);
			if (model == null) RedirectToAction<ClassesController>(
				 c => c.Index());
			var pageClassId = _PageGroupService.GetPageGroupById(model.PageGroupId);

			create_page_group_elements(pageClassId.ClassId, pageClassId.Name);

			ViewData.Model = model;
			return View();
		}


		[AcceptVerbs(HttpVerbs.Post)]
		[ValidateInput(false)]
		public ActionResult UpdateLabWork(FormCollection values)
		{
			var update = _LabWorkArticleService.GetLabWorkArticleById(
			new Guid(
			values[MemberUtil<PageArticle>.MemberName(m => m.ID)]));
			if (update == null) return RedirectToAction<ClassesController>(c => c.Index());
			TryUpdateModel(update, new string[]{
				MemberUtil<LabWorkArticle>.MemberName(m=>m.ID),
				MemberUtil<LabWorkArticle>.MemberName(m=>m.PageGroupId),
				MemberUtil<LabWorkArticle>.MemberName(m=>m.Text),
				MemberUtil<LabWorkArticle>.MemberName(m=>m.Title),
				MemberUtil<LabWorkArticle>.MemberName(m=>m.Publish),
				MemberUtil<LabWorkArticle>.MemberName(m=>m.SubmissionEnd)
			}, values.ToValueProvider());

			if (ModelState.IsValid)
			{
				_PageArticleService.SaveOrUpdate(update);
				var pageGroup = _PageGroupService.GetPageGroupById(update.PageGroupId);
				return RedirectToAction<ClassesController>(
					c => c.EditOrCreateContent(pageGroup.ClassId));

			}
			var _pageGroup = _PageGroupService.GetPageGroupById(update.PageGroupId);

			create_page_group_elements(_pageGroup.ClassId, null);
			return View();
		}
		[AcceptVerbs( HttpVerbs.Post)]
		public ActionResult RemoveLabWork(Guid id)
		{
			var entity = _LabWorkArticleService.GetLabWorkArticleById(id);
			
			if (entity != null && entity.Username == User.Identity.Name)
			{
				var classId = _PageGroupService.GetPageGroupById(entity.PageGroupId).ClassId;
				_LabWorkArticleService.Remove(entity);
				return RedirectToAction<ClassesController>(c=>c.EditOrCreateContent(classId));
			}
			
				return RedirectToAction<ClassesController>(c => c.Index());
		}

		#endregion

		#region Utility
		private void create_page_group_elements(Guid classId, string selected)
		{
			var pageGroups = _PageGroupService.GetPageGroupsByClassId(classId);
			IEnumerable<SelectListItem> items = (pageGroups.ToSelectListItems(selected));
			ViewData["classId"] = classId;
			ViewData.Add("pageGroups", items);

		}
		#endregion
	}
	#region Extension Helper
	internal static class HelperExtension
	{
		internal static IEnumerable<SelectListItem> ToSelectListItems(this IList<PageGroup> pageGroups, string selected)
		{
			IEnumerable<SelectListItem> items = pageGroups.Select(it => new SelectListItem { Value = it.ID.ToString(), Text = it.Name, Selected = it.Name == selected }).AsEnumerable();
			return items;
		}
	}
	#endregion
}
